﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using Loki.IoC;
using Loki.IoC.Registration;
using Loki.Utils;

namespace Loki.UI
{
    public class BootStrapperAdapter<TMainViewModel> : LokiObject, IHandle<StartMessage>, IHandle<ExitMessage>, IHandle<ErrorMessage>, IHandle<InformationMessage>, IHandle<WarningMessage>
        where TMainViewModel : class, IScreen
    {
        public BootStrapperAdapter(IPlatform application)
        {
            try
            {
                Application = application;

                UIContext.Configure(application);

                if (!UIContext.Windows.DesignMode)
                {
                    ConventionManager = new DefaultConventionManager();

                    Application.Signals.LastChanceError += Application_UnhandledException;
                    Application.Signals.ApplicationStart += Signals_ApplicationStart;
                }
            }
            catch (Exception exception)
            {
                Log.Error(exception.Message, exception);
                throw;
            }
        }

        /// <summary>
        /// Gets or sets the application model installers.
        /// </summary>
        public IEnumerable<IContextInstaller> ServiceInstallers { get; set; }

        /// <summary>
        /// Gets or sets the data model installers.
        /// </summary>
        public IEnumerable<IContextInstaller> DataInstallers { get; set; }

        /// <summary>
        /// Gets or sets the UI model installers.
        /// </summary>
        public IEnumerable<IContextInstaller> UIInstallers { get; set; }

        protected IPlatform Application { get; private set; }

        protected IConventionManager ConventionManager { get; set; }

        protected IEnumerable<Assembly> SelectedAssemblies
        {
            get
            {
                return new Assembly[] { Assembly.GetEntryAssembly(), Assembly.GetExecutingAssembly() }.Distinct();
            }
        }

        public void Application_UnhandledException(object P_Sender, ExceptionEventArgs e)
        {
            Application.Signals.Error(e.Exception, true);
        }

        public void Initialize(string[] startParameters)
        {
            OnInitializing(EventArgs.Empty);

            try
            {
                Toolkit.MessageBus.Subscribe(this);

                // Initialize Data engine
                if (DataInstallers != null)
                {
                    Toolkit.IoC.DefaultContext.Initialize(DataInstallers.ToArray());
                }

                // Initialize application engine
                if (ServiceInstallers != null)
                {
                    Toolkit.IoC.DefaultContext.Initialize(ServiceInstallers.ToArray());
                }

                // Initialize ui engine
                if (UIInstallers == null)
                {
                    // no specific configuration : configure default TMainViewModel
                    Toolkit.IoC.DefaultContext.Register(Element.For<TMainViewModel>());
                }
                else
                {
                    Toolkit.IoC.DefaultContext.Initialize(UIInstallers.ToArray());
                }

                Application.Templates.LoadByConvention(ConventionManager, SelectedAssemblies.ToArray());

                OnInitialized(EventArgs.Empty);
                PreStart(startParameters);
            }
            catch (Exception ex)
            {
                Application.Signals.Error(ex, true);
                Exit(-1);
            }
        }

        protected virtual void Exit(int returnCode)
        {
            Toolkit.MessageBus.Unsubscribe(this);
            Application.Signals.ApplicationExit(returnCode);
        }

        protected virtual void PreStart(string[] startParameters)
        {
            /*if (Application.SplashModel == null)
            {*/
            Toolkit.MessageBus.PublishOnUIThread(new StartMessage(startParameters));

            /*}
            else
            {
                var splashScreen = ToolKit.Templates.GetView(Application.SplashModel as IViewModel);
                splashScreen.Show();
                bool buffer = Application.SplashModel.StartLoading();
                if (buffer)
                {
                    ApplicationCommands.START.Execute(null);
                }
                else
                {
                    ApplicationCommands.EXIT.Execute(null);
                }
            }*/
        }

        public void Signals_ApplicationStart(object sender, StartupEventArgs e)
        {
            Initialize(e.Parameters);
        }

        #region Initializing

        public event EventHandler Initializing;

        protected virtual void OnInitializing(EventArgs e)
        {
            EventHandler L_Handler = Initializing;

            if (L_Handler != null)
            {
                L_Handler(this, e);
            }
        }

        #endregion Initializing

        #region Initialized

        public event EventHandler Initialized;

        protected virtual void OnInitialized(EventArgs e)
        {
            EventHandler L_Handler = Initialized;

            if (L_Handler != null)
            {
                L_Handler(this, e);
            }
        }

        #endregion Initialized

        public void Start()
        {
            // Creates main objects
            TMainViewModel mainModel = Toolkit.IoC.DefaultContext.Get<TMainViewModel>();

            Application.MainObject = mainModel;

            mainModel.Activate();
        }

        #region Message handlers

        public void Handle(ErrorMessage message)
        {
            Application.Signals.Error(message.Error, false);
        }

        public void Handle(ExitMessage message)
        {
            Application.Signals.ApplicationExit(message.ErrorCode);
        }

        public void Handle(InformationMessage message)
        {
            Application.Signals.Message(string.Format(CultureInfo.CurrentUICulture, Resources.Formats.Message_Format, message.Message));
        }

        public void Handle(StartMessage message)
        {
            Start();
        }

        public void Handle(WarningMessage message)
        {
            Application.Signals.Warning(string.Format(CultureInfo.CurrentUICulture, Resources.Formats.Message_Format, message.Message));
        }

        #endregion Message handlers

        #region ViewModels

        /*
        /// <summary>
        /// Gets the view models.
        /// </summary>
        public BindableCollection<IViewModel> ViewModels
        {
            get;
            private set;
        }

        protected TViewModel ActivateOrAdd<TViewModel>(Func<TViewModel, bool> P_Selector, Action<TViewModel> P_Initializer) where TViewModel : class,IViewModel
        {
            // search
            TViewModel L_Model = default(TViewModel);
            if (P_Selector != null)
            {
                L_Model = ViewModels.OfType<TViewModel>().FirstOrDefault(x => P_Selector(x));
            }
            else
            {
                L_Model = ViewModels.OfType<TViewModel>().FirstOrDefault();
            }

            // init
            if (L_Model == null)
            {
                L_Model = AddNew<TViewModel>(P_Initializer);
            }
            else
            {
                L_Model.Activate.Execute(null);
            }

            return L_Model;
        }

        protected TViewModel AddNew<TViewModel>(Action<TViewModel> P_Initializer) where TViewModel : class,IViewModel
        {
            TViewModel L_NewModel = ToolKit.Engine.UIContext.Get<TViewModel>();
            if (P_Initializer != null)
            {
                P_Initializer(L_NewModel);
            }

            AddSubModel(L_NewModel);
            return L_NewModel;
        }

        private void AddSubModel(IViewModel P_Model)
        {
            Commands.Handle(P_Model.Close, (s, e) => SubModel_CanClose(P_Model, e), (s, e) => SubModel_Close(P_Model, e));
            ViewModels.Add(P_Model);

            IView L_View = ToolKit.Templates.GetView(P_Model);
            if (L_View != null)
            {
                L_View.Show();
            }
        }

        private void SubModel_CanClose(IViewModel P_Model, CanExecuteCommandEventArgs e)
        {
            e.CanExecute = true;
        }

        private void SubModel_Close(IViewModel P_Model, CommandEventArgs P_Args)
        {
            Commands.UnHandle(P_Model.Close);
            P_Model.DoCleanup();
            ViewModels.Remove(P_Model);
        }
        */

        #endregion ViewModels
    }
}